<!-- eslint-disable vue/multi-word-component-names -->
<script>
import { GlBadge, GlLink, GlSprintf, GlFriendlyWrap } from '@gitlab/ui';
import { s__ } from '~/locale';
import { SEVERITY_LEVELS_GRAPHQL } from 'ee/security_dashboard/store/constants';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import FalsePositiveAlert from 'ee/vulnerabilities/components/false_positive_alert.vue';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import GenericReportSection from 'ee/vulnerabilities/components/generic_report/report_section_graphql.vue';
import VulnerabilityDetail from 'ee/vue_shared/security_reports/components/vulnerability_detail.vue';
import VulnerabilityTraining from 'ee/vulnerabilities/components/vulnerability_training.vue';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { getHttpString } from 'ee/vue_shared/security_reports/components/helpers';
import {
  SUPPORTING_MESSAGE_TYPES,
  VULNERABILITY_TRAINING_HEADING,
} from 'ee/vulnerabilities/constants';
import { REPORT_TYPES_TO_HUMAN_READABLE } from './constants';
import DetailsSection from './details_section.vue';
import DetailsSectionListItem from './details_section_list_item.vue';

export default {
  directives: { SafeHtml },
  i18n: {
    descriptionSectionHeading: s__('Vulnerability|Description'),
    severityLabel: s__('Vulnerability|Severity:'),
    stateLabel: s__('Vulnerability|Status:'),
    projectLabel: s__('Vulnerability|Project:'),
    toolLabel: s__('Vulnerability|Tool:'),
    scannerLabel: s__('Vulnerability|Scanner:'),
    evidenceLabel: s__('Vulnerability|Evidence:'),
    locationHeading: s__('Vulnerability|Location'),
    locationFileLabel: s__('Vulnerability|File:'),
    locationImageLabel: s__('Vulnerability|Image:'),
    locationOperatingSystemLabel: s__('Vulnerability|Namespace:'),
    locationCrashTypeLabel: s__('Vulnerability|Crash type:'),
    locationCrashAddressLabel: s__('Vulnerability|Crash address:'),
    locationStacktraceSnippetLabel: s__('Vulnerability|Stacktrace snippet:'),
    locationVulnerableClassLabel: s__('Vulnerability|Vulnerable class:'),
    locationVulnerableMethodLabel: s__('Vulnerability|Vulnerable method:'),
    linksSectionHeading: s__('Vulnerability|Links'),
    identifiersSectionHeading: s__('Vulnerability|Identifiers'),
    assetsSectionHeading: s__('Vulnerability|Reproduction Assets'),
    requestResponseHeading: s__('Vulnerability|Request/Response'),
    requestLabel: s__('Vulnerability|Sent request:'),
    responseLabel: s__('Vulnerability|Actual response:'),
    recordedResponseLabel: s__('Vulnerability|Unmodified response:'),
    additionalInfoHeading: s__('Vulnerability|Additional Info'),
    assertLabel: s__('Vulnerability|Assert:'),
    requestUrlLabel: s__('Vulnerability|URL:'),
    VULNERABILITY_TRAINING_HEADING,
  },
  components: {
    VulnerabilityDetail,
    VulnerabilityTraining,
    GlBadge,
    GlLink,
    GlSprintf,
    GlFriendlyWrap,
    SeverityBadge,
    DetailsSection,
    DetailsSectionListItem,
    FalsePositiveAlert,
    GenericReportSection,
    CodeBlock,
  },
  inheritAttrs: false,
  props: {
    description: {
      type: String,
      required: false,
      default: '',
    },
    descriptionHtml: {
      type: String,
      required: false,
      default: '',
    },
    state: {
      type: String,
      required: false,
      default: '',
    },
    severity: {
      type: String,
      required: true,
      validator: (v) => SEVERITY_LEVELS_GRAPHQL.includes(v),
    },
    project: {
      type: Object,
      required: true,
    },
    evidence: {
      type: Object,
      required: false,
      default: null,
    },
    location: {
      type: Object,
      required: false,
      default: null,
    },
    reportType: {
      type: String,
      required: false,
      default: '',
    },
    links: {
      type: Array,
      required: false,
      default: () => [],
    },
    identifiers: {
      type: Array,
      required: false,
      default: () => [],
    },
    scanner: {
      type: Object,
      required: false,
      default: null,
    },
    assets: {
      type: Array,
      required: false,
      default: () => [],
    },
    falsePositive: {
      type: Boolean,
      required: true,
    },
    details: {
      type: Array,
      required: false,
      default: () => [],
    },
  },
  data() {
    return {
      showTraining: false,
    };
  },
  computed: {
    humanReadableReportType() {
      return REPORT_TYPES_TO_HUMAN_READABLE[this.reportType];
    },
    lineNumber() {
      const startLine = this.location?.startLine ?? null;
      const endLine = this.location?.endLine ?? null;

      if (startLine === null && endLine === null) {
        return '';
      }

      if (startLine < endLine) {
        return `${startLine}-${endLine}`;
      }

      return startLine;
    },
    file() {
      const { blobPath, file } = this.location || {};
      const { lineNumber } = this;

      const name = file ? `${file}${lineNumber && `:${lineNumber}`}` : '';
      const url = blobPath ? `${blobPath}${lineNumber && `#L${lineNumber}`}` : '';

      return {
        url,
        name,
      };
    },
    requestString() {
      const { method, url, headers } = this.evidence?.request || {};

      return method && url && headers?.length ? getHttpString(this.evidence?.request) : '';
    },
    requestUrl() {
      return this.evidence?.request?.url || this.fileLocation;
    },
    fileLocation() {
      const { location: { hostname = '', path = '' } = {} } = this;

      return hostname + path;
    },
    responseString() {
      const { statusCode, headers } = this.evidence?.response || {};

      return statusCode && headers?.length ? getHttpString(this.evidence?.response) : '';
    },
    assertion() {
      return this.evidence?.source?.name;
    },
    evidenceSummary() {
      return this.evidence?.summary;
    },
    recordedResponseString() {
      const recordedResponse = this.evidence?.supportingMessages?.find(
        (msg) => msg.name === SUPPORTING_MESSAGE_TYPES.RECORDED,
      )?.response;
      return getHttpString(recordedResponse);
    },
    projectFullPath() {
      return this.project?.fullPath;
    },
    shouldShowLocationSection() {
      const { file, location = {} } = this;
      const hasLocationData = (field) => location[field];

      return (
        file.name ||
        [
          'image',
          'operatingSystem',
          'crashType',
          'crashAddress',
          'stacktraceSnippet',
          'vulnerableClass',
          'vulnerableMethod',
        ].some(hasLocationData)
      );
    },
  },
  methods: {
    handleShowTraining(showVulnerabilityTraining) {
      this.showTraining = showVulnerabilityTraining;
    },
  },
};
</script>

<template>
  <article class="gl-display-flex gl-flex-direction-column gl-gap-3">
    <false-positive-alert v-if="falsePositive" class="gl-mb-4" />

    <details-section
      v-if="description || descriptionHtml"
      :heading="$options.i18n.descriptionSectionHeading"
      data-testid="description-section"
    >
      <div v-if="descriptionHtml" v-safe-html="descriptionHtml"></div>
      <p v-else>{{ description }}</p>
    </details-section>

    <details-section data-testid="main-section">
      <template #list>
        <details-section-list-item
          v-if="state"
          :label="$options.i18n.stateLabel"
          data-testid="state-list-item"
        >
          <gl-badge variant="warning" class="text-capitalize">{{ state }}</gl-badge>
        </details-section-list-item>

        <details-section-list-item
          :label="$options.i18n.severityLabel"
          data-testid="severity-list-item"
        >
          <severity-badge class="gl-display-inline-block gl-ml-2" :severity="severity" />
        </details-section-list-item>

        <details-section-list-item
          :label="$options.i18n.projectLabel"
          data-testid="project-list-item"
        >
          <gl-link :href="project.webUrl">{{ project.name }}</gl-link>
        </details-section-list-item>

        <details-section-list-item
          v-if="humanReadableReportType"
          :label="$options.i18n.toolLabel"
          data-testid="report-type-list-item"
        >
          {{ humanReadableReportType }}
        </details-section-list-item>

        <details-section-list-item
          v-if="scanner"
          :label="$options.i18n.scannerLabel"
          data-testid="scanner-list-item"
        >
          <gl-sprintf
            v-if="scanner.version"
            :message="s__('Vulnerability|%{scannerName} (version %{scannerVersion})')"
          >
            <template #scannerName>{{ scanner.name }}</template>
            <template #scannerVersion>{{ scanner.version }}</template>
          </gl-sprintf>
          <template v-else>{{ scanner.name }}</template>
        </details-section-list-item>

        <details-section-list-item
          v-if="requestUrl"
          :label="$options.i18n.requestUrlLabel"
          data-testid="request-url-list-item"
        >
          <gl-link :href="requestUrl" target="_blank">
            <gl-friendly-wrap :text="requestUrl" />
          </gl-link>
        </details-section-list-item>

        <details-section-list-item
          v-if="evidenceSummary"
          :label="$options.i18n.evidenceLabel"
          data-testid="evidence-list-item"
        >
          {{ evidenceSummary }}
        </details-section-list-item>
      </template>
    </details-section>

    <details-section
      v-if="shouldShowLocationSection"
      :heading="$options.i18n.locationHeading"
      data-testid="location-section"
    >
      <template #list>
        <details-section-list-item
          v-if="file.name"
          :label="$options.i18n.locationFileLabel"
          data-testid="location-file-list-item"
        >
          <component :is="file.url ? 'gl-link' : 'span'" :href="file.url">
            {{ file.name }}
          </component>
        </details-section-list-item>
        <details-section-list-item
          v-if="location.image"
          :label="$options.i18n.locationImageLabel"
          data-testid="location-image-list-item"
        >
          {{ location.image }}
        </details-section-list-item>
        <details-section-list-item
          v-if="location.operatingSystem"
          :label="$options.i18n.locationOperatingSystemLabel"
          data-testid="location-operating-system-list-item"
        >
          {{ location.operatingSystem }}
        </details-section-list-item>
        <details-section-list-item
          v-if="location.crashType"
          :label="$options.i18n.locationCrashTypeLabel"
          data-testid="location-crash-type-list-item"
        >
          {{ location.crashType }}
        </details-section-list-item>
        <details-section-list-item
          v-if="location.crashAddress"
          :label="$options.i18n.locationCrashAddressLabel"
          data-testid="location-crash-address-list-item"
        >
          {{ location.crashAddress }}
        </details-section-list-item>
        <details-section-list-item
          v-if="location.stacktraceSnippet"
          :label="$options.i18n.locationStacktraceSnippetLabel"
          data-testid="location-stack-trace-snippet-list-item"
        >
          <code-block :code="location.stacktraceSnippet" max-height="225px" />
        </details-section-list-item>
        <details-section-list-item
          v-if="location.vulnerableClass"
          :label="$options.i18n.locationVulnerableClassLabel"
          data-testid="location-vulnerable-class-list-item"
        >
          {{ location.vulnerableClass }}
        </details-section-list-item>
        <details-section-list-item
          v-if="location.vulnerableMethod"
          :label="$options.i18n.locationVulnerableMethodLabel"
          data-testid="location-vulnerable-method-list-item"
        >
          {{ location.vulnerableMethod }}
        </details-section-list-item>
      </template>
    </details-section>

    <details-section
      v-if="links.length"
      :heading="$options.i18n.linksSectionHeading"
      data-testid="links-section"
    >
      <ul class="gl-p-0">
        <li v-for="link in links" :key="link.url" class="gl-list-style-position-inside">
          <gl-link :href="link.url">
            {{ link.name || link.url }}
          </gl-link>
        </li>
      </ul>
    </details-section>

    <details-section
      v-if="identifiers.length"
      :heading="$options.i18n.identifiersSectionHeading"
      data-testid="identifiers-section"
    >
      <ul class="gl-p-0">
        <li
          v-for="identifier in identifiers"
          :key="identifier.name"
          class="gl-list-style-position-inside"
        >
          <gl-link v-if="identifier.url" :href="identifier.url">
            {{ identifier.name }}
          </gl-link>
          <template v-else>{{ identifier.name }}</template>
        </li>
      </ul>
    </details-section>

    <details-section
      v-if="requestString || responseString || recordedResponseString"
      :heading="$options.i18n.requestResponseHeading"
      data-testid="request-response-section"
    >
      <template #list>
        <details-section-list-item
          v-if="requestString"
          :label="$options.i18n.requestLabel"
          data-testid="request-item"
        >
          <code-block class="gl-mt-3" :code="requestString" max-height="225px" />
        </details-section-list-item>

        <details-section-list-item
          v-if="recordedResponseString"
          :label="$options.i18n.recordedResponseLabel"
          data-testid="recorded-response-item"
        >
          <code-block class="gl-mt-3" :code="recordedResponseString" max-height="225px" />
        </details-section-list-item>

        <details-section-list-item
          v-if="responseString"
          :label="$options.i18n.responseLabel"
          data-testid="response-item"
        >
          <code-block class="gl-mt-3" :code="responseString" max-height="225px" />
        </details-section-list-item>
      </template>
    </details-section>

    <details-section
      v-if="assertion"
      :heading="$options.i18n.additionalInfoHeading"
      data-testid="additional-info-section"
    >
      <template #list>
        <details-section-list-item :label="$options.i18n.assertLabel" data-testid="assert-item">
          {{ assertion }}
        </details-section-list-item>
      </template>
    </details-section>

    <details-section
      v-if="assets.length"
      :heading="$options.i18n.assetsSectionHeading"
      data-testid="assets-section"
    >
      <ul class="gl-p-0">
        <li v-for="asset in assets" :key="asset.name" class="gl-list-style-position-inside">
          <gl-link v-if="asset.url" :href="asset.url">
            {{ asset.name }}
          </gl-link>
          <template v-else>{{ asset.name }}</template>
        </li>
      </ul>
    </details-section>

    <generic-report-section :report-items="details" />

    <details-section
      v-if="identifiers.length > 0 && projectFullPath"
      v-show="showTraining"
      :heading="$options.i18n.VULNERABILITY_TRAINING_HEADING.title"
      data-testid="training"
    >
      <vulnerability-training
        :project-full-path="projectFullPath"
        :identifiers="identifiers"
        :file="location.file"
        class="gl-mb-6"
        @show-vulnerability-training="handleShowTraining"
      />
    </details-section>
  </article>
</template>
